import gradio as gr
import pandas as pd
import numpy as np
import plotly.graph_objects as go
import matplotlib.pyplot as plt
import matplotlib
import os

from copy import deepcopy
from utils import en_to_cn_20 as en_to_cn
from utils import meta_dic_153_20, meta_dic_20_153


print(os.getcwd())

def process_matrix(data, row=True, col=True):
    # 按行合并
    # if row:
    #     for k, li in meta_dic_20_153.items():
    #         print(k,li)
    #         data.loc[k,:] = data.loc[li].sum()
    #         for i in li:
    #             data.drop(i, inplace=True, axis=0)
    # # 按列合并
    # if col:
    #     for k, li in meta_dic_20_153.items():
    #         data.loc[:,k] = data.loc[:,li].sum(axis=1)
    #         for i in li:
    #             data.drop(i, inplace=True, axis=1)

    # 单位变成万亿元
    data = data / 100000000
    data = data.round(3)
    return data


class Table:
    def __init__(self, data, name):
        self.en_table = data
        self.cn_table = self.get_cn_table(name)
        self.cn_table = self.en_table
        
    
    def get_cn_table(self, name=None):
        if isinstance(self.en_table, pd.DataFrame):
            tb = deepcopy(self.en_table)
            if tb.shape[1] == 20:  
                tb.columns = [en_to_cn[i] for i in tb.columns]
            if tb.shape[0] == 20:
                tb.index = [en_to_cn[i] for i in tb.index]
        else:
            tb = deepcopy(self.en_table)
            if name is not None:
                tb.name = name
            else:
                tb.name = en_to_cn[tb.name]
            if len(tb) == 20:
                tb.index = [en_to_cn[i] for i in tb.index]
        return tb
    
    def get_plot_se(self, key, col_plot):
        if isinstance(self.cn_table, pd.Series):
            se = self.cn_table
            se = se.sort_values(ascending=False)
        else:
            if key in self.cn_table.columns or key in self.cn_table.index:
                if col_plot:
                    se = self.cn_table.loc[:,key]
                else:
                    se = self.cn_table.loc[key,:]
            elif key in self.en_table.columns or key in self.en_table.index:
                if col_plot:
                    se = self.en_table.loc[:,key]
                else:
                    se = self.en_table.loc[key,:]
            else:
                se = pd.Series()
            print(se)
            se = se.sort_values(ascending=False)
        return se
    
    def se_plot(self, key, title, col_plot=True):
        se = self.get_plot_se(key, col_plot)
        # 创建示例数据
        x = se.index.to_list()
        y = se.to_list()
        # 创建柱状图
        fig = go.Figure(data=[go.Bar(x=x, y=y)])
        # 设置图表标题和坐标轴标签
        fig.update_layout(title=f"{title}",xaxis_title='类别',yaxis_title='数值')
        return fig


class InputOutputTable:
    def __init__(self):
        self._map = {'matrix':'中间消耗矩阵',
                     'gdp_exp':'支出法矩阵',
                     'gdp_income':'收入法矩阵',
                     'total_input':'总投入',
                     'total_output':'总产出',
                     'matrix_ratio':'直接消耗系数矩阵',
                     'complete_matrix_ratio':'完全消耗系数矩阵',
                     'influence_coef':'影响力系数',
                     'sensitivity_coef':'感应度系数',
                     'Leontief_r_matrix':'列昂惕夫逆矩阵'
                     }
        self.en_to_cn = en_to_cn
        self.all_data = pd.read_excel(r'data/2020中国投入产出表.xlsx', sheet_name=None, index_col=0)
        # 中间消耗矩阵
        self.matrix = Table(process_matrix(data=self.all_data['中间消耗矩阵'], row=True, col=True), '')
        # 支出法矩阵
        self.gdp_exp =Table(process_matrix(data=self.all_data['支出法矩阵'], row=True, col=False), '') 
        # 生产法矩阵

        self.gdp_income = Table(process_matrix(data=self.all_data['收入法矩阵'], row=False, col=True), '总投入')
        # 总投入 向量
        self.total_input = Table(self.matrix.en_table.sum(axis=0) + self.gdp_income.en_table.sum(axis=0), name='总产出')
        # 总产出 向量
        self.total_output = Table(self.gdp_exp.en_table['总产出'], name='总产出')
        # 直接消耗系数矩阵
        self.matrix_ratio = Table(self.matrix.en_table / self.total_input.en_table, '')
        # 完全消耗系数矩阵
        self.complete_matrix_ratio = Table(self.complete_matrix(), '')
        # 列昂惕夫逆矩阵
        self.Leontief_r_matrix = Table(self.L_r_m(), '列昂惕夫逆矩阵')
        # 影响力系数, 后向关联效应衡量的是一个产业作为使用者，对其上游供应者产业的影响。它反映了该产业的产品被其他产业作为最终产品使用的程度。
        self.influence_coef = Table(self.influence_or_sensitivity_coefficient(inf=True),  '影响力系数') 
        # 感应度系数, 前向关联效应衡量的是一个产业作为供应者，对其下游使用者产业的影响。它反映了该产业的产品被其他产业作为中间投入品使用的程度。
        self.sensitivity_coef = Table(self.influence_or_sensitivity_coefficient(inf=False),  '感应度系数')

    def validate_iodata(self):
        a = self.total_input.en_table - self.total_output.en_table
        if a.sum() < 1:
            print('数据验证通过')
        else:
            print('数据验证失败')
            
    def Leontief_reversed_matrix(self):
        # 计算列昂惕夫逆矩阵
        A = self.matrix_ratio.en_table.to_numpy()
        n = A.shape[0]
        I = np.eye(n)
        liatif= np.linalg.inv(I-A)
        print('列昂惕夫逆矩阵：\n',liatif.shape)
        return liatif
    
    def L_r_m(self):
        L = self.Leontief_reversed_matrix()
        return pd.DataFrame(L, index=self.matrix.en_table.index, columns=self.matrix.en_table.columns)
        
    
    def complete_matrix(self):
        temp_matrix = self.Leontief_reversed_matrix() - np.eye(self.matrix.en_table.shape[0])
        return pd.DataFrame(temp_matrix, index=self.matrix.en_table.index, columns=self.matrix.en_table.columns)
    
    def influence_or_sensitivity_coefficient(self, inf=True):
        # 影响力系数，后向关联（行求和） 或 感应度系数，前向关联（列求和)
        data = self.Leontief_reversed_matrix()
        df = pd.DataFrame(data, index=self.matrix.en_table.index, columns=self.matrix.en_table.columns)
        if inf:
            df = df.sum(axis=0)
        else:
            df = df.sum(axis=1)
        return df / df.mean()

        

class ShowIOData:
    def __init__(self, io: InputOutputTable):
        self.io = io
        self.block_matrix = {
            '中间消耗矩阵（第一象限）': self.io.matrix,
            '直接消耗系数': self.io.matrix_ratio,
            '完全消耗系数': self.io.complete_matrix_ratio,
            '最终需求矩阵（支出法GDP）': self.io.gdp_exp,
            '初始投入矩阵（收入法GDP）': self.io.gdp_income,
            '总产出': self.io.total_output,
            '总投入': self.io.total_input,
            '影响力系数': self.io.influence_coef,
            '感应度系数': self.io.sensitivity_coef,
            '列昂惕夫逆矩阵': self.io.Leontief_r_matrix,
        }

        self.block_unit = {
            '中间消耗矩阵（第一象限）': '万亿元',
            '直接消耗系数': '比例',
            '完全消耗系数': '比例',
            '最终需求矩阵（支出法GDP）': '万亿元',
            '初始投入矩阵（收入法GDP）': '万亿元',
            '总产出': '万亿元',
            '总投入': '万亿元',
            '影响力系数': '比例',
            '感应度系数': '比例',
            '列昂惕夫逆矩阵': '比例',
        }
        self.blocks = list(self.block_matrix.keys())
        self.row_names = self.io.matrix.cn_table.index.tolist()
        self.col_names = self.io.matrix.cn_table.columns.tolist()
    
    @staticmethod
    def add_data(data):
        data_ = deepcopy(pd.DataFrame(data))
        if '列名' not in data_.columns:
            data_.insert(0, '列名', data_.index)
        return data_
    
    def update_col_img(self, matrix_name, col_name):
        matrix_or_se = self.block_matrix[matrix_name]
        title = f"{matrix_name}按照列【{col_name}】展开，单位：{self.block_unit[matrix_name]}"
        fig = matrix_or_se.se_plot(col_name, title=title, col_plot=True)
        return fig, self.add_data(matrix_or_se.cn_table) 

    def update_row_img(self, matrix_name, row_name):
        matrix_or_se = self.block_matrix[matrix_name]
        title = f"{matrix_name}按照行【{row_name}】展开，单位：{self.block_unit[matrix_name]}"
        fig = matrix_or_se.se_plot(row_name, title, col_plot=False)
        return fig, self.add_data(matrix_or_se.cn_table)
       
    def select_matrix(self, matrix_name):
        matrix_or_se = self.block_matrix[matrix_name]
        if isinstance(matrix_or_se.cn_table, pd.DataFrame):
            row_name = matrix_or_se.cn_table.index.to_list()
            col_name = matrix_or_se.cn_table.columns.to_list()
        else:
            row_name = matrix_or_se.cn_table.index.to_list()
            col_name = [matrix_or_se.cn_table.name]
        r_dropdown = gr.Dropdown(choices=row_name,label='行名',value=row_name[0],interactive=True)
        c_dropdown = gr.Dropdown(choices=col_name, label='列名',value=col_name[0],interactive=True)
        
        return r_dropdown, c_dropdown


    def html(self):
        with gr.Blocks() as demo:
            gr.Markdown("# 投入产出表")
            with gr.Row():
                with gr.Column(scale=1):
                    matrix_name = gr.Dropdown(label='矩阵', choices=self.blocks, value=self.blocks[0])
                    row_name = gr.Dropdown(label='行名', choices=self.row_names, value=self.row_names[0],interactive=True)
                    col_name = gr.Dropdown(label='列名', choices=self.col_names, value=self.col_names[0],interactive=True)
                    
                with gr.Column(scale=3):
                    plot = gr.Plot(label='图')
            
            with gr.Row():
                df_show = gr.DataFrame(label='表格')
                
            matrix_name.change(self.select_matrix, inputs=[matrix_name], outputs=[row_name, col_name])
            row_name.change(self.update_row_img, inputs=[matrix_name, row_name], outputs=[plot, df_show])
            col_name.change(self.update_col_img, inputs=[matrix_name, col_name], outputs=[plot, df_show])

        return demo




if __name__ == '__main__':
    # 以向量的方式展示所有的矩阵
    io = InputOutputTable()
    # show = ShowIOData(io)
    # show.html().launch()
